package com.bosssoft.cloud.wechat.core.infra.util;

import com.bosssoft.cloud.wechat.infrastructure.enums.ErrEnum;
import com.bosssoft.cloud.wechat.infrastructure.exception.SystemException;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.UUID;

/**
 * 微信签名校验工具类
 *
 * @author wcy create on 2021/5/28 10:27 上午
 */
@Slf4j
public class WxSignatureUtil {


    /**
     * 开发者签名校验
     * <p>
     * 校验流程：
     * 1.对token，timestamp,nonce进行字典序排序并按序拼接
     * 2.对拼接好的str做sha1签名加密
     * 3.对加密后的tmpStr与传入的signature签名比较，相同则校验成功
     *
     * @param mpToken   微信token
     * @param signature 签名
     * @param timestamp 时间戳
     * @param nonce     随机字符串
     * @return 校验结果
     */
    public static boolean checkSignature(String mpToken, String signature, String timestamp, String nonce) {
        String[] arr = new String[]{mpToken, timestamp, nonce};
        // 将token, timestamp, nonce三个参数进行字典序排序
        Arrays.sort(arr);
        StringBuilder content = new StringBuilder();
        for (String s : arr) {
            content.append(s);
        }
        // sha1加密
        String tmpStr = getSha1(content.toString());
        // 将sha1加密后的字符串于signature对比，标识该请求来源于微信
        return tmpStr != null && tmpStr.equals(signature);
    }

    /**
     * 签名算法
     * 主要在公众号前端要调用微信的JS-SDK的API时，获取签名授权
     *
     * @param jsapi_ticket 调用微信JS接口的临时票据
     * @param noncestr     随机字符串
     * @param timestamp    时间戳
     * @param url          当前网页的URL，不包含#及其后面部分
     * @return 签名signature
     */
    public static String getSignature(
            String jsapi_ticket,
            String noncestr,
            String timestamp,
            String url
    ) {
        // 字典序（拼接字符串数量很少完全不需要StringBuilder）
        return getSha1("jsapi_ticket=" + jsapi_ticket +
                "&noncestr=" + noncestr +
                "&timestamp=" + timestamp +
                "&url=" + url);
    }

    /**
     * Sha1签名加密
     *
     * @param str 要加密的str
     * @return 加密后的字符串
     */
    public static String getSha1(String str) {
        log.info("SHA1签名加密参数：{}", str);
        if (str == null || str.length() == 0) {
            return null;
        }
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
                'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes(StandardCharsets.UTF_8));
            byte[] md = mdTemp.digest();
            char[] buf = new char[md.length * 2];
            int k = 0;
            for (byte byte0 : md) {
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf);
        } catch (NoSuchAlgorithmException e) {
            throw new SystemException(ErrEnum.B0421);
        }
    }

    /**
     * 生成nonce
     *
     * @return nonce
     */
    public static String generateNonceStr() {
        return UUID.randomUUID().toString().replaceAll("-", "");
    }

    /**
     * 生成timestamp
     *
     * @return timestamp
     */
    public static long getCurrentTimestamp() {
        return System.currentTimeMillis() / 1000;
    }
}
